package model

import (
	"database/sql"
	"fmt"
	"github.com/tal-tech/go-zero/core/stores/cache"
	"github.com/tal-tech/go-zero/core/stores/sqlc"
	"github.com/tal-tech/go-zero/core/stores/sqlx"
	"github.com/tal-tech/go-zero/core/stringx"
	"github.com/tal-tech/go-zero/tools/goctl/model/sql/builderx"
	"strings"
	"time"
)

var (
	tbStatFieldNames          = builderx.RawFieldNames(&TbStat{})
	tbStatRows                = strings.Join(tbStatFieldNames, ",")
	tbStatRowsExpectAutoSet   = strings.Join(stringx.Remove(tbStatFieldNames, "`id`", "`create_time`", "`update_time`"), ",")
	tbStatRowsWithPlaceHolder = strings.Join(stringx.Remove(tbStatFieldNames, "`id`", "`create_time`", "`update_time`"), "=?,") + "=?"

	cacheTbStatIdPrefix = "cache::tbStat:id:"
)

type (
	TbStatModel interface {
		Insert(data TbStat) (sql.Result, error)
		FindOne(id int64) (*TbStat, error)
		Update(data TbStat) error
		Delete(id int64) error
		StatEveryDay() error
	}

	defaultTbStatModel struct {
		sqlc.CachedConn
		table string
	}

	TbStat struct {
		Id     int64        `db:"id"`
		Sn     string       `db:"sn"`     // 设备唯一号
		Posno  string       `db:"posno"`  // 终端编号
		City   string       `db:"city"`   // 城市代码
		Tyid   string       `db:"tyid"`   // 设备类型
		Sver   string       `db:"sver"`   // 软件版本号
		Carno  string       `db:"carno"`  // 车辆编号
		Unum1  int64        `db:"unum1"`  // 未传记录数--公交
		Unum2  int64        `db:"unum2"`  // 未传记录数--三方
		Ndate  sql.NullTime `db:"ndate"`  // 当前日期
		Ntime  string       `db:"ntime"`  // 当前时间
		Amount int64        `db:"amount"` // 当班总额
		Count  int64        `db:"count"`  // 当班人数
		Line   int64        `db:"line"`   // 线路编号
		Stime  string       `db:"stime"`  // 开机时间
		Ctime  string       `db:"ctime"`  // 关机时间
		Pver   int64        `db:"pver"`   // 票价版本
		Bver   int64        `db:"bver"`   // 黑名单版本
		Empid  string       `db:"empid"`  // 司机编号
		Jd     string       `db:"jd"`     // 经度
		Wd     string       `db:"wd"`     // 维度
		Rvs1   string       `db:"rvs1"`   // 保留
		Rvs2   string       `db:"rvs2"`   // 保留备用
		Tenant int64        `db:"tenant"` // 租户号
		Alarm  int64        `db:"alarm"`  // 设备报警码
		Cmd    int64        `db:"cmd"`    // 控制命令
	}
)

func NewTbStatModel(conn sqlx.SqlConn, c cache.CacheConf) TbStatModel {
	return &defaultTbStatModel{
		CachedConn: sqlc.NewConn(conn, c),
		table:      "`tb_stat`",
	}
}

func (m *defaultTbStatModel) Insert(data TbStat) (sql.Result, error) {
	query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", m.table, tbStatRowsExpectAutoSet)
	ret, err := m.ExecNoCache(query, data.Sn, data.Posno, data.City, data.Tyid, data.Sver, data.Carno, data.Unum1, data.Unum2, data.Ndate, data.Ntime, data.Amount, data.Count, data.Line, data.Stime, data.Ctime, data.Pver, data.Bver, data.Empid, data.Jd, data.Wd, data.Rvs1, data.Rvs2, data.Tenant, data.Alarm, data.Cmd)

	return ret, err
}

func (m *defaultTbStatModel) FindOne(id int64) (*TbStat, error) {
	tbStatIdKey := fmt.Sprintf("%s%v", cacheTbStatIdPrefix, id)
	var resp TbStat
	err := m.QueryRow(&resp, tbStatIdKey, func(conn sqlx.SqlConn, v interface{}) error {
		query := fmt.Sprintf("select %s from %s where `id` = ? limit 1", tbStatRows, m.table)
		return conn.QueryRow(v, query, id)
	})
	switch err {
	case nil:
		return &resp, nil
	case sqlc.ErrNotFound:
		return nil, ErrNotFound
	default:
		return nil, err
	}
}

func (m *defaultTbStatModel) Update(data TbStat) error {
	tbStatIdKey := fmt.Sprintf("%s%v", cacheTbStatIdPrefix, data.Id)
	_, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
		query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, tbStatRowsWithPlaceHolder)
		return conn.Exec(query, data.Sn, data.Posno, data.City, data.Tyid, data.Sver, data.Carno, data.Unum1, data.Unum2, data.Ndate, data.Ntime, data.Amount, data.Count, data.Line, data.Stime, data.Ctime, data.Pver, data.Bver, data.Empid, data.Jd, data.Wd, data.Rvs1, data.Rvs2, data.Tenant, data.Alarm, data.Cmd, data.Id)
	}, tbStatIdKey)
	return err
}

func (m *defaultTbStatModel) Delete(id int64) error {

	tbStatIdKey := fmt.Sprintf("%s%v", cacheTbStatIdPrefix, id)
	_, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
		query := fmt.Sprintf("delete from %s where `id` = ?", m.table)
		return conn.Exec(query, id)
	}, tbStatIdKey)
	return err
}

func (m *defaultTbStatModel) formatPrimary(primary interface{}) string {
	return fmt.Sprintf("%s%v", cacheTbStatIdPrefix, primary)
}

func (m *defaultTbStatModel) queryPrimary(conn sqlx.SqlConn, v, primary interface{}) error {
	query := fmt.Sprintf("select %s from %s where `id` = ? limit 1", tbStatRows, m.table)
	return conn.QueryRow(v, query, primary)
}

/**
每日状态统计
*/

func (m *defaultTbStatModel) StatEveryDay() error {

	var result []*TbStatus
	var count int64

	limit := 50
	//page := 1

	t := time.Now().Format("2006-01-02")
	t1, _ := time.Parse("2006-01-02", t)

	queryCount := fmt.Sprintf("select count(1) as count FROM %s WHERE id IN (SELECT MAX(id) FROM %s GROUP BY sn) AND ndate = ?", "tb_status", "tb_status")
	err := m.CachedConn.QueryRowNoCache(&count, queryCount, t)
	if err != nil {
		return err
	}

	pageCount := int(count) / limit
	if int(count)%limit != 0 {
		pageCount += 1
	}

	for page := 1; page <= pageCount; page++ {
		query := fmt.Sprintf("select * FROM %s WHERE id IN (SELECT MAX(id) FROM %s GROUP BY sn) AND ndate = ? limit ?,?", "tb_status", "tb_status")
		err = m.CachedConn.QueryRowsNoCache(&result, query, t, (page-1)*limit, limit)
		if err != nil {
			return err
		}
		for _, val := range result {
			st := TbStat{}
			st.Sn = val.Sn
			st.Posno = val.Posno
			st.City = val.City
			st.Tyid = val.Tyid
			st.Sver = val.Sver
			st.Carno = val.Carno
			st.Unum1 = val.Unum1
			st.Unum2 = val.Unum2

			st.Ndate = sql.NullTime{t1, true}

			st.Ntime = val.Ntime
			st.Amount = val.Amount
			st.Count = val.Count
			st.Line = val.Line
			st.Stime = val.Stime
			st.Ctime = val.Ctime
			st.Pver = val.Pver
			st.Bver = val.Bver
			st.Empid = val.Empid
			st.Jd = val.Jd
			st.Wd = val.Wd
			st.Rvs1 = val.Rvs1
			st.Rvs2 = val.Rvs2
			st.Tenant = val.Tenant
			st.Alarm = val.Alarm
			st.Cmd = val.Cmd

			_, err := m.Insert(st)
			if err != nil {
				return err
			}
		}
	}

	return err
}
